package com.info.zhiduoduo.pay.service;

import com.baomidou.mybatisplus.mapper.Condition;
import com.info.zhiduoduo.common.constants.Constants;
import com.info.zhiduoduo.common.constants.SmartResult;
import com.info.zhiduoduo.common.constants.StatusMsg;
import com.info.zhiduoduo.common.exception.SmartException;
import com.info.zhiduoduo.common.utils.DateUtil;
import com.info.zhiduoduo.common.utils.LogUtil;
import com.info.zhiduoduo.common.utils.NumberUtil;
import com.info.zhiduoduo.common.utils.RandomUtil;
import com.info.zhiduoduo.common.utils.StringUtil;
import com.info.zhiduoduo.pay.exceptionhandler.GlobalExceptionHandler;
import com.info.zhiduoduo.pay.formbean.wxpay.WXPayFromBean;
import com.info.zhiduoduo.pay.util.MD5Util;
import com.info.zhiduoduo.repository.entity.company.CompanyProject;
import com.info.zhiduoduo.repository.entity.invoice.Invoice;
import com.info.zhiduoduo.repository.entity.maker.Maker;
import com.info.zhiduoduo.repository.entity.order.OrderProduct;
import com.info.zhiduoduo.repository.entity.taxer.TaxerPayment;
import com.info.zhiduoduo.repository.entity.weixin.WxpayRecord;
import com.info.zhiduoduo.repository.mapper.company.CompanyProjectMapper;
import com.info.zhiduoduo.repository.mapper.invoice.InvoiceMapper;
import com.info.zhiduoduo.repository.mapper.order.OrderProductMapper;
import com.info.zhiduoduo.repository.mapper.taxer.TaxerPaymentMapper;
import com.info.zhiduoduo.repository.mapper.weixin.WxpayRecordMapper;
import com.info.zhiduoduo.wxsdk.WxPayApiService;
import com.info.zhiduoduo.wxsdk.config.WxTaxerConfig;
import com.info.zhiduoduo.wxsdk.config.WxTaxerPayConfig;
import com.info.zhiduoduo.wxsdk.config.WxZddMakerConfig;
import com.info.zhiduoduo.wxsdk.config.WxZddMakerPayConfig;
import com.info.zhiduoduo.wxsdk.domain.pay.WxUnifiedOrderData;
import com.info.zhiduoduo.wxsdk.domain.pay.WxUnifiedOrderResult;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

@Service
@Transactional
public class WeChatPayService {

	@Autowired
	WxZddMakerConfig wxZddMakerConfig;

	@Autowired
	WxZddMakerPayConfig wxZddMakerPayConfig;

	@Autowired
	WxTaxerConfig wxTaxerConfig;

	@Autowired
	WxTaxerPayConfig wxTaxerPayConfig;

	@Autowired
	WxpayRecordMapper wxpayRecordMapper;

	@Resource
	OrderProductMapper orderProductMapper;

	@Autowired
	TaxerPaymentMapper taxerPaymentMapper;

	@Autowired
	InvoiceMapper invoiceMapper;

	@Autowired
	CompanyProjectMapper projectMapper;

	@Value("${wechatPay.notifyUrl}")
	private String notifyUrl;

	public static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

	//微信付款
	public SmartResult weChatPay(WXPayFromBean formBean, String buyerIp) {
		if (null == formBean || StringUtil.isAnyEmpty(formBean.getOpenid(), formBean.getOrderDesc(), formBean.getOrderNo()) ||
				NumberUtil.isAnyZero(formBean.getOrderPrice())) {
			return SmartResult.ofErrorParam();
		}
		//订单号
		String orderNo = formBean.getOrderNo();
		//订单类型 1：杰税侠订单
		int orderType = formBean.getOrderType();
		//用户的openid
		String openid = formBean.getOpenid();
		//价格
		int orderPrice = formBean.getOrderPrice();
		//商品描述
		String body = formBean.getOrderDesc();

		//根据不同的订单类型，生成微信支付服务
		WxPayApiService wxPayApiService;
		if (orderType == 1) {
			wxPayApiService = WxPayApiService.init(wxTaxerPayConfig);
		} else {
			wxPayApiService = WxPayApiService.init(wxZddMakerPayConfig);
		}

		WxpayRecord payRecord = wxpayRecordMapper.findLastOneByOrderNoAndOpenid(formBean.getOrderNo(), formBean.getOpenid());
		if (payRecord != null) {
			//已成功的就直接返回
			Integer status = payRecord.getStatus();
			if (status == Constants.WXPAY_COMPLETED) {
				return SmartResult.fail(StatusMsg.StatusMsg_449);
			} else if (status == Constants.WXPAY_OVER_TIME || status == Constants.WXPAY_FAIL) {
				//超时或失败，重新生成一笔支付记录
				payRecord = new WxpayRecord().setOrderNo(orderNo)
						.setOrderType(orderType)
						.setPayNo(RandomUtil.createRandomNo("WXPAY_", ""))
						.setBody(body)
						.setPrice(orderPrice)
						.setOpenid(openid)
						.setCreatetime(new Date());
				payRecord.insert();
			} else if (status == Constants.WXPAY_WATING_PAY || status == Constants.WXPAY_CREATED) {
				//已创建或支付中，就是用这笔订单重新支付

			}
		} else {
			payRecord = new WxpayRecord().setOrderNo(orderNo)
					.setOrderType(orderType)
					.setPayNo(RandomUtil.createRandomNo("WXPAY_", ""))
					.setBody(body)
					.setPrice(orderPrice)
					.setOpenid(openid)
					.setCreatetime(new Date());
			payRecord.insert();

		}

		try {

			String total_fee = String.valueOf(orderPrice);
			// //测试使用1分钱
			// total_fee = "1";

			WxUnifiedOrderData unifiyOrderData = new WxUnifiedOrderData();
			unifiyOrderData.setBody(body)
					.setOut_trade_no(payRecord.getPayNo())
					.setTotal_fee(total_fee)
					.setOpenid(openid)
					.setNotify_url(notifyUrl);
			WxUnifiedOrderResult unifiedResult = wxPayApiService.unifiedOrder(unifiyOrderData);
			if (unifiedResult == null) {
				return SmartResult.fail(StatusMsg.StatusMsg_445);
			}
			if ("FAIL".equals(unifiedResult.getReturn_code())) {
				String returnmsg = unifiedResult.getReturn_msg();
				LogUtil.info("统一下单错误：" + returnmsg);
				payRecord.setStatus(Constants.WXPAY_FAIL)
						.setErrCode(unifiedResult.getErr_code())
						.setErrCodeDes(unifiedResult.getErr_code_des())
						.updateById();
				return SmartResult.fail(StatusMsg.StatusMsg_446);
			} else if ("SUCCESS".equals(unifiedResult.getReturn_code()) && "FAIL".equals(unifiedResult.getResult_code())) {
				LogUtil.info("统一下单错误：" + unifiedResult.getErr_code());
				payRecord.setStatus(Constants.WXPAY_FAIL)
						.setErrCode(unifiedResult.getErr_code())
						.setErrCodeDes(unifiedResult.getErr_code_des())
						.updateById();
				return SmartResult.fail(StatusMsg.StatusMsg_446);
			}

			long tsStr = new Date().getTime() / 1000;
			payRecord.setPrepayId(unifiedResult.getPrepay_id()).setStatus(Constants.WXPAY_WATING_PAY).updateById();

			Map<String, String> returndata = new HashMap<>();
			returndata.put("appId", unifiedResult.getAppid());
			returndata.put("package", "prepay_id=" + unifiedResult.getPrepay_id());
			returndata.put("nonceStr", unifiedResult.getNonce_str());
			returndata.put("signType", "MD5");
			returndata.put("timeStamp", tsStr + "");
			returndata.put("sign", wxPayApiService.generateSignature(returndata));
			return SmartResult.success(returndata);

		} catch (Exception e) {
			LogUtil.info(GlobalExceptionHandler.getExceptionDetail(e));
		}

		return SmartResult.fail(StatusMsg.StatusMsg_445);
	}

	public String notifyPayResult(HttpServletRequest request) {
		try {
			InputStream inStream = request.getInputStream();
			ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			outSteam.close();
			inStream.close();

			String result = new String(outSteam.toByteArray(), "utf-8");
			System.out.println("result-----------------" + result);

			SortedMap<String, String> map = dom4jXMLParse(result);

			if (map != null) {
				String return_code = map.get("return_code");
				String result_code = map.get("result_code");
				if ("SUCCESS".equals(return_code)) {
					String outTradeNo = map.get("out_trade_no");
					if (StringUtil.isNotEmpty(outTradeNo)) {

						WxpayRecord payRecord = wxpayRecordMapper.findOneByPayNo(outTradeNo);
						Integer orderType = payRecord.getOrderType();
						String orderNo = payRecord.getOrderNo();

						//根据不同的订单类型，生成微信支付服务
						WxPayApiService wxPayApiService;
						if (orderType == 1) {
							wxPayApiService = WxPayApiService.init(wxTaxerPayConfig);
						} else {
							wxPayApiService = WxPayApiService.init(wxZddMakerPayConfig);
						}

						//验证签名
						boolean isWechatSign = wxPayApiService.isPayResultNotifySignatureValid(map);
						if (!isWechatSign) return "";

						payRecord.setResultCode(result_code);
						if ("SUCCESS".equals(result_code)) {

							Date paytime = DateUtil.parseDate(map.get("time_end"), DateUtil.FORMAT_STYLE_7);
							payRecord.setStatus(Constants.WXPAY_COMPLETED)
									.setTransactionId(map.get("transaction_id"))
									.setPaytime(paytime)
									.updateById();

							if (orderType == 1 || orderType == 2) {
								//杰税侠订单
								handleTaxerPay(orderNo, paytime);
							} else if (orderType == 3) {
								handleOfflineProjectInvoicePay(orderNo);
							} else if (orderType == 4) {
								handleProductPay(orderNo, paytime);
							}
						} else {
							//支付业务失败，微信支付记录标记为失败状态
							payRecord.setStatus(Constants.WXPAY_FAIL)
									.setErrCode(map.get("err_code"))
									.setErrCodeDes(map.get("err_code_des"))
									.updateById();
						}

						//成功后返回的信息
						return "<xml>\n" + "\n" + "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
								"  <return_msg><![CDATA[OK]]></return_msg>\n" + "</xml>";

					} else {

						return "";
					}
				} else {
					return "";
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return "";
	}

	private void handleTaxerPay(String orderNo, Date paytime) {
		TaxerPayment taxerPayment = taxerPaymentMapper.findOneByPayno(orderNo);
		if (taxerPayment != null) {
			taxerPayment.setState(2).setConfirmtime(paytime).updateById();

			Integer type = taxerPayment.getType();
			if (type == Constants.TAXER_PAY_TYPE_VERIFY) {
				//杰税侠注册，更新账户的到期日期。
				Date expireDate = DateUtil.localDate2Date(
						DateUtil.date2LocalDate(paytime).plusYears(1).minusDays(1));

				new Maker().setUserid(taxerPayment.getUserid())
						.setIssueDate(paytime)
						.setExpireDate(expireDate)
						.updateById();
			}
		}
	}

	private void handleProductPay(String orderNo, Date paytime) {
		OrderProduct orderProduct = orderProductMapper.getOneOrderProduct(orderNo);
		if (orderProduct != null) {
			//订单状态 1：已创建 2：已支付
			orderProduct.setState(2)
					.setPaytime(paytime)
					.updateById();

		}
	}

	private void handleOfflineProjectInvoicePay(String orderNo) {
		Invoice invoice = new Invoice().selectOne(Condition.create().eq("applyno", orderNo));
		if (invoice == null) throw SmartException.of(StatusMsg.StatusMsg_326);

		invoice.setPaytime(new Date()).setOfflinePayState(Constants.OFFLINE_PAID).updateById();
		new CompanyProject().setProjectid(invoice.getProjectid()).setOfflinePayState(Constants.OFFLINE_PAID).updateById();
	}

	/**
	 * 传入微信回调返回的XML信息
	 * 以Map形式返回便于取值
	 * dom4j解析XML,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值为空
	 *
	 * @param strXML
	 * @return
	 * @throws DocumentException
	 */
	@SuppressWarnings("rawtypes")
	public static SortedMap<String, String> dom4jXMLParse(String strXML) throws DocumentException {
		SortedMap<String, String> smap = new TreeMap<String, String>();
		Document doc = DocumentHelper.parseText(strXML);
		Element root = doc.getRootElement();
		for (Iterator iterator = root.elementIterator(); iterator.hasNext(); ) {
			Element e = (Element) iterator.next();
			smap.put(e.getName(), e.getText());
		}
		return smap;
	}

	/**
	 * 是否微信V3签名,规则是:按参数名称a-z排序,遇到空值的参数不参加签名
	 * 传入微信返回信息解析后的SortedMap格式参数数据
	 * 验证消息是否是微信发出的合法消息
	 *
	 * @param smap
	 * @param apiKey 设置的密钥
	 * @return 验证结果
	 */
	@SuppressWarnings("rawtypes")
	public static boolean isWechatSign(SortedMap<String, String> smap, String apiKey) {
		StringBuffer sb = new StringBuffer();
		Set es = smap.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if (!"sign".equals(k) && null != v && !"".equals(v) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + apiKey);
		/** 验证的签名 */
		String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
		/** 微信端返回的合法签名 */
		String validSign = ((String) smap.get("sign")).toUpperCase();
		return validSign.equals(sign);
	}
}
